#pragma once

/**
 * @file channel.h
 * @brief 参照 Go 语言语义实现的 CSP 通道（chan）。
 *
 * 主要功能：
 *   - 支持缓冲 / 无缓冲通道；
 *   - 多生产者、多消费者安全并发；
 *   - close() 后唤醒阻塞的 send / recv；
 *   - Go 风格命名：send() 相当于 `ch <- v`，recv() 相当于 `<-ch`。
 *
 * 注意：由于是模板类，实现直接放在头文件中。
 */

#include <condition_variable>
#include <deque>
#include <mutex>
#include <optional>
#include <memory>

namespace go {

/**
 * @tparam T 通道中传递的数据类型。
 */
template <typename T>
class chan {
public:
    /**
     * @brief 创建通道。
     * @param capacity 缓冲区容量。0 表示无缓冲通道（同步 / Rendezvous）。
     */
    explicit chan(std::size_t capacity = 0)
        : capacity_(capacity) {}

    // 禁用拷贝，允许移动（与 Go 中 channel 引用类型类似）
    chan(const chan&) = delete;
    chan& operator=(const chan&) = delete;

    /**
     * @brief 向通道发送数据，相当于 Go 的 `ch <- value`。
     * @param value 待发送的数据（会发生移动或复制）。
     * @return 成功返回 true；若通道已关闭，返回 false。
     *
     * - 无缓冲通道：阻塞等待接收者到来，实现握手。
     * - 有缓冲通道：阻塞直到缓冲区有空位。
     * - close() 后，立即返回 false。
     */
    bool send(T value) {
        std::unique_lock<std::mutex> lock(mutex_);

        if (closed_) {
            return false;
        }

        if (capacity_ == 0) {
            // 无缓冲：等待 slot 空闲
            cv_send_.wait(lock, [this] {
                return closed_ || !slot_.has_value();
            });
            if (closed_) {
                return false;
            }

            // 放入临时槽位，唤醒接收者
            slot_.emplace(std::move(value));
            cv_recv_.notify_one();

            // 等待接收者取走
            cv_send_.wait(lock, [this] {
                return closed_ || !slot_.has_value();
            });
            return !closed_;
        }

        // 有缓冲：等待缓冲区不满
        cv_send_.wait(lock, [this] {
            return closed_ || buffer_.size() < capacity_;
        });
        if (closed_) {
            return false;
        }

        buffer_.push_back(std::move(value));
        cv_recv_.notify_one();
        return true;
    }

    /**
     * @brief 从通道接收数据，相当于 Go 的 `value, ok := <-ch`。
     * @param out 输出参数，用于接收数据。
     * @return 若成功获取数据返回 true；若通道关闭且数据已耗尽返回 false。
     */
    bool recv(T& out) {
        std::unique_lock<std::mutex> lock(mutex_);

        if (capacity_ == 0) {
            cv_recv_.wait(lock, [this] {
                return closed_ || slot_.has_value();
            });

            if (!slot_.has_value()) {
                // 通道已关闭且无数据
                return false;
            }

            out = std::move(*slot_);
            slot_.reset();
            cv_send_.notify_one();
            return true;
        }

        cv_recv_.wait(lock, [this] {
            return closed_ || !buffer_.empty();
        });

        if (buffer_.empty()) {
            // 通道关闭且缓冲区已清空
            return false;
        }

        out = std::move(buffer_.front());
        buffer_.pop_front();
        cv_send_.notify_one();
        return true;
    }

    /**
     * @brief 关闭通道，等价于 Go 的 `close(ch)`。
     *
     * - close 后禁止继续 send；
     * - 已缓冲的数据仍可被 recv 取完；
     * - 多次调用是幂等的。
     */
    void close() {
        {
            std::lock_guard<std::mutex> lock(mutex_);
            if (closed_) {
                return;
            }
            closed_ = true;
        }
        cv_send_.notify_all();
        cv_recv_.notify_all();
    }

    /**
     * @brief 查询通道是否已关闭。
     */
    bool is_closed() const {
        std::lock_guard<std::mutex> lock(mutex_);
        return closed_;
    }

private:
    const std::size_t capacity_;          // 缓冲容量
    mutable std::mutex mutex_;            // 保护下述成员
    std::condition_variable cv_send_;     // 发送端条件变量
    std::condition_variable cv_recv_;     // 接收端条件变量
    std::deque<T> buffer_;                // 缓冲区，capacity_ > 0 时使用
    std::optional<T> slot_;               // 无缓冲通道的临时槽
    bool closed_{false};                  // 是否已关闭
};

/**
 * @brief Go 风格的创建函数：`auto ch = go::make_chan<int>(1);`
 */
template <typename T>
std::shared_ptr<chan<T>> make_chan(std::size_t capacity = 0) {
    return std::make_shared<chan<T>>(capacity);
}

} // namespace go